/*-------------------------------------------------------------------------
 *
 * dest.c
 *	  支持通信目标
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/tcop/dest.c
 *
 *-------------------------------------------------------------------------
 */
/*
 *	 接口例程
 *		BeginCommand - 在命令开始时初始化目标
 *		CreateDestReceiver - 为目标创建元组接收器对象
 *		EndCommand - 在命令结束时清理目标
 *		NullCommand - 告诉目标已识别到一个空查询字符串
 *		ReadyForQuery - 告诉目标我们准备好进行新的查询
 *
 *	 注意事项
 *		这些例程在查询返回元组之前和之后执行相应的工作，以保持后端
 *		和“目标”门户的同步。
 */

#include "postgres.h"

#include "access/printsimple.h"
#include "access/printtup.h"
#include "access/xact.h"
#include "commands/copy.h"
#include "commands/createas.h"
#include "commands/matview.h"
#include "executor/functions.h"
#include "executor/tqueue.h"
#include "executor/tstoreReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/portal.h"


/* ----------------
 *		虚拟 DestReceiver 函数
 * ----------------
 */
static bool fc_donothingReceive(TupleTableSlot *fc_slot, DestReceiver *fc_self)
{
	return true;
}

static void fc_donothingStartup(DestReceiver *fc_self, int fc_operation, TupleDesc fc_typeinfo)
{
}

static void fc_donothingCleanup(DestReceiver *fc_self)
{
	/* 这用于关闭和销毁方法 */
}

/* ----------------
 *		针对不需要本地状态的目标类型的静态 DestReceiver 结构
 * ----------------
 */
static const DestReceiver donothingDR = {
	fc_donothingReceive, fc_donothingStartup, fc_donothingCleanup, fc_donothingCleanup,
	DestNone
};

static const DestReceiver debugtupDR = {
	debugtup, debugStartup, fc_donothingCleanup, fc_donothingCleanup,
	DestDebug
};

static const DestReceiver printsimpleDR = {
	printsimple, printsimple_startup, fc_donothingCleanup, fc_donothingCleanup,
	DestRemoteSimple
};

static const DestReceiver spi_printtupDR = {
	spi_printtup, spi_dest_startup, fc_donothingCleanup, fc_donothingCleanup,
	DestSPI
};

/*
 * 对于 DestNone 的全局可用接收器。
 *
 * 可以去除 const 修饰，因为对 none 接收器的任何修改都将是一个错误
 * （这样更容易捕捉到错误）。
 */
DestReceiver *None_Receiver = (DestReceiver *) &donothingDR;

/* ----------------
 *		BeginCommand - 在命令开始时初始化目标
 * ----------------
 */
void BeginCommand(CommandTag fc_commandTag, CommandDest fc_dest)
{
	/* 目前没有要做的事情 */
}

/* ----------------
 *		CreateDestReceiver - 返回适当的接收器函数集用于目标
 * ----------------
 */
DestReceiver * CreateDestReceiver(CommandDest fc_dest)
{
	/*
	 * 可以去除 const 修饰，因为对 none
	 * 接收器的任何修改都将是一个错误（这样更容易捕捉到错误）。
	 */

	switch (fc_dest)
	{
		case DestRemote:
		case DestRemoteExecute:
			return printtup_create_DR(fc_dest);

		case DestRemoteSimple:
			return unconstify(DestReceiver *, &printsimpleDR);

		case DestNone:
			return unconstify(DestReceiver *, &donothingDR);

		case DestDebug:
			return unconstify(DestReceiver *, &debugtupDR);

		case DestSPI:
			return unconstify(DestReceiver *, &spi_printtupDR);

		case DestTuplestore:
			return CreateTuplestoreDestReceiver();

		case DestIntoRel:
			return CreateIntoRelDestReceiver(NULL);

		case DestCopyOut:
			return CreateCopyDestReceiver();

		case DestSQLFunction:
			return CreateSQLFunctionDestReceiver();

		case DestTransientRel:
			return CreateTransientRelDestReceiver(InvalidOid);

		case DestTupleQueue:
			return CreateTupleQueueDestReceiver(NULL);
	}

	/* 不应该到这里 */
	pg_unreachable();
}

/* ----------------
 *		EndCommand - 在命令结束时清理目标
 * ----------------
 */
void EndCommand(const QueryCompletion *fc_qc, CommandDest fc_dest, bool fc_force_undecorated_output)
{
	char		fc_completionTag[COMPLETION_TAG_BUFSIZE];
	CommandTag	fc_tag;
	const char *fc_tagname;

	switch (fc_dest)
	{
		case DestRemote:
		case DestRemoteExecute:
		case DestRemoteSimple:

			/*
			 * 我们假定 tagname 是纯 ASCII，因此不需要
			 * 编码转换。
			 *
			 * 我们不再显示 LastOid，但为了保持 wire
			 * 协议，我们在 LastOid 原本写入的位置写入 InvalidOid。
			 *
			 * 所有写入 LastOid 的情况也写入 nprocessed
			 * 计数，因此只需断言这一点，而不是再进行额外的测试。
			 */
			fc_tag = fc_qc->commandTag;
			fc_tagname = GetCommandTagName(fc_tag);

			if (command_tag_display_rowcount(fc_tag) && !fc_force_undecorated_output)
				snprintf(fc_completionTag, COMPLETION_TAG_BUFSIZE,
						 fc_tag == CMDTAG_INSERT ?
						 "%s 0 " UINT64_FORMAT : "%s " UINT64_FORMAT,
						 fc_tagname, fc_qc->nprocessed);
			else
				snprintf(fc_completionTag, COMPLETION_TAG_BUFSIZE, "%s", fc_tagname);
			pq_putmessage('C', fc_completionTag, strlen(fc_completionTag) + 1);

		case DestNone:
		case DestDebug:
		case DestSPI:
		case DestTuplestore:
		case DestIntoRel:
		case DestCopyOut:
		case DestSQLFunction:
		case DestTransientRel:
		case DestTupleQueue:
			break;
	}
}

/* ----------------
 *		EndReplicationCommand - 精简版的 EndCommand
 *
 *		供复制命令使用。
 * ----------------
 */
void EndReplicationCommand(const char *fc_commandTag)
{
	pq_putmessage('C', fc_commandTag, strlen(fc_commandTag) + 1);
}

/* ----------------
 *		NullCommand - 告诉目标已识别到一个空查询字符串
 *
 *		这确保对扩展查询协议中的 Execute 消息的响应将有一个可识别的结尾。
 * ----------------
 */
void NullCommand(CommandDest fc_dest)
{
	switch (fc_dest)
	{
		case DestRemote:
		case DestRemoteExecute:
		case DestRemoteSimple:

			/* 告诉 FE 我们看到了一个空查询字符串 */
			pq_putemptymessage('I');
			break;

		case DestNone:
		case DestDebug:
		case DestSPI:
		case DestTuplestore:
		case DestIntoRel:
		case DestCopyOut:
		case DestSQLFunction:
		case DestTransientRel:
		case DestTupleQueue:
			break;
	}
}

/* ----------------
 *		ReadyForQuery - 告诉目标我们准备好进行新的查询
 *
 *		ReadyForQuery 消息被发送，以便 FE 能够判断我们何时完成处理查询字符串。
 *		在版本 3.0 及以上，它还携带事务状态指示符。
 *
 *		请注意，通过在这里刷新 stdio 缓冲区，我们可以避免在其他大多数地方执行
 *		此操作，从而减少发送的单独数据包的数量。
 * ----------------
 */
void ReadyForQuery(CommandDest fc_dest)
{
	switch (fc_dest)
	{
		case DestRemote:
		case DestRemoteExecute:
		case DestRemoteSimple:
			{
				StringInfoData fc_buf;

				pq_beginmessage(&fc_buf, 'Z');
				pq_sendbyte(&fc_buf, TransactionBlockStatusCode());
				pq_endmessage(&fc_buf);
			}
			/* 在周期结束时无论如何刷新输出。 */
			pq_flush();
			break;

		case DestNone:
		case DestDebug:
		case DestSPI:
		case DestTuplestore:
		case DestIntoRel:
		case DestCopyOut:
		case DestSQLFunction:
		case DestTransientRel:
		case DestTupleQueue:
			break;
	}
}
